package com.xiyuan.smartutils.crypto;


import com.xiyuan.smartutils.reflect.Unsafe;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;

/**
 * AES 加密工具
 *
 * @version v1.0.0 @author lgz 2021-8-4 新建与整理
 */
public class AES {
    static {
        Unsafe.init();
    }
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    public static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    
    
    private static Cipher getCipher(byte[] keyBytes, String cipherAlgorithm, int mode, byte[] iv) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
        
        // Cipher完成加密或解密工作类
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        
        String _mode = cipherAlgorithm.toLowerCase();
        if (_mode.indexOf("/ecb") != -1) {
            // 对Cipher初始化加解密模式
            cipher.init(mode, secretKey);
        }
        else if (_mode.indexOf("/cbc/") != -1
                || _mode.indexOf("/cfb") != -1
                || _mode.indexOf("/ofb") != -1
                || _mode.indexOf("/ctr") != -1
        ) {
            // Generate IV // Typically filled with random bytes
            if (iv == null) {
                iv = new byte[16];
            }
            
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            cipher.init(mode, secretKey, ivSpec);
        }
        
        
        // 加密data
        return cipher;
    }
    
    
    /**
     * 加密
     *
     * @param payload         待加密内容
     * @param key             加密码
     * @param cipherAlgorithm 加密算法
     * @return 加密结果
     * @throws Exception 异常信息
     */
    public static byte[] encrypt(byte[] payload, byte[] key, String cipherAlgorithm) throws Exception {
        return getCipher(key, cipherAlgorithm, Cipher.ENCRYPT_MODE, null).doFinal(payload);
    }
    
    /**
     * 加密
     *
     * @param payload         待加密内容
     * @param key             加密密码
     * @param cipherAlgorithm 加密算法
     * @param iv      初始化向量
     * @return 加密结果
     * @throws Exception 异常信息
     */
    public static byte[] encrypt(byte[] payload, byte[] key, String cipherAlgorithm, byte[] iv) throws Exception {
        return getCipher(key, cipherAlgorithm, Cipher.ENCRYPT_MODE, iv).doFinal(payload);
    }
    
    /**
     * 加密 使用默认算法 AES/ECB/PKCS5Padding
     *
     * @param payload 待加密内容
     * @param key     加密密码
     * @return 加密结果
     * @throws Exception 异常信息
     */
    public static byte[] encrypt(byte[] payload, byte[] key) throws Exception {
        return encrypt(payload, key, DEFAULT_CIPHER_ALGORITHM);
    }
    
    /**
     * 加密 使用默认算法 AES/ECB/PKCS5Padding
     *
     * @param payload 待加密内容
     * @param key     加密码
     * @param iv      初始化向量
     * @return 加密结果
     * @throws Exception 异常信息
     */
    public static byte[] encrypt(byte[] payload, byte[] key, byte[] iv) throws Exception {
        return encrypt(payload, key, DEFAULT_CIPHER_ALGORITHM, iv);
    }
    
    /***********************************************************************************/
    /************************  解密  ****************************************************/
    /***********************************************************************************/
    
    /**
     * 解密
     *
     * @param payload         待解密内容
     * @param key             解密密码
     * @param cipherAlgorithm 解密算法
     * @return 解密结果
     * @throws Exception 异常
     */
    public static byte[] decrypt(byte[] payload, byte[] key, String cipherAlgorithm) throws Exception {
        return getCipher(key, cipherAlgorithm, Cipher.DECRYPT_MODE, null).doFinal(payload);
    }
    
    /**
     * 解密
     *
     * @param payload         待解密内容
     * @param key             解密密码
     * @param cipherAlgorithm 解密算法
     * @return 解密结果
     * @throws Exception 异常
     */
    public static byte[] decrypt(byte[] payload, byte[] key, String cipherAlgorithm, byte[] iv) throws Exception {
        return getCipher(key, cipherAlgorithm, Cipher.DECRYPT_MODE, iv).doFinal(payload);
    }
    
    /**
     * 解密 默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待解密内容
     * @param key     解密码
     * @return 解密结果
     * @throws Exception 异常
     */
    public static byte[] decrypt(byte[] payload, byte[] key) throws Exception {
        return getCipher(key, DEFAULT_CIPHER_ALGORITHM, Cipher.DECRYPT_MODE, null).doFinal(payload);
    }
    
    
    /**
     * 解密 默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待解密内容
     * @param key     解密码
     * @param iv      初始化向量
     * @return 解密结果
     * @throws Exception 异常
     */
    public static byte[] decrypt(byte[] payload, byte[] key, byte[] iv) throws Exception {
        return getCipher(key, DEFAULT_CIPHER_ALGORITHM, Cipher.DECRYPT_MODE, iv).doFinal(payload);
    }
    
    
    /**
     * 加密，返回加密后的字符串,并且使用 UTF-8 编码
     *
     * @param payload         待加密内容
     * @param key             加密码
     * @param cipherAlgorithm 加密算法
     * @return hex 字符串
     * @throws Exception 异常信息
     */
    public static String encode(String payload, String key, String cipherAlgorithm) throws Exception {
        Charset charset = DEFAULT_CHARSET;
        byte[] encoded = encrypt(payload.getBytes(charset), key.getBytes(charset), cipherAlgorithm);
        return HEX.toString(encoded);
    }
    
    /**
     * 加密，返回加密后的字符串,并且使用 UTF-8 编码
     *
     * @param payload         待加密内容
     * @param key             加密码
     * @param cipherAlgorithm 加密算法
     * @param iv      初始化向量
     * @return hex 字符串
     * @throws Exception 异常信息
     */
    public static String encode(String payload, String key, String cipherAlgorithm, byte[] iv) throws Exception {
        Charset charset = DEFAULT_CHARSET;
        byte[] encoded = encrypt(payload.getBytes(charset), key.getBytes(charset), cipherAlgorithm, iv);
        return HEX.toString(encoded);
    }
    
    /**
     * 加密，返回加密后的字符串,并且使用 UTF-8编码, 默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待加密内容
     * @param key     加密码
     * @return hex 字符串
     * @throws Exception 异常信息
     */
    public static String encode(String payload, String key) throws Exception {
        Charset charset = DEFAULT_CHARSET;
        return HEX.toString(encrypt(payload.getBytes(charset), key.getBytes(charset)));
    }
    
    /**
     * 加密，返回加密后的字符串,并且使用 UTF-8编码, 默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待加密内容
     * @param key     加密码
     * @param iv      初始化向量
     * @return hex 字符串
     * @throws Exception 异常信息
     */
    public static String encode(String payload, String key, byte[] iv) throws Exception {
        Charset charset = DEFAULT_CHARSET;
        return HEX.toString(encrypt(payload.getBytes(charset), key.getBytes(charset), iv));
    }
    
    
    /**
     * 解密，参数为 AES 加密后的hex内容,并且使用 UTF-8编码
     *
     * @param payload         待解密的hex 字符串
     * @param key             解密密码
     * @param cipherAlgorithm 解密算法
     * @return 解密后的内容
     * @throws Exception 异常
     */
    public static String decode(String payload, String key, String cipherAlgorithm) throws Exception {
        byte[] bytes = HEX.toBytes(payload);
        byte[] keyBytes = key.getBytes(DEFAULT_CHARSET);
        return new String(decrypt(bytes, keyBytes, cipherAlgorithm));
    }
    
    /**
     * 解密，参数为 AES 加密后的hex内容,并且使用 UTF-8编码
     *
     * @param payload         待解密的hex 字符串
     * @param key             解密密码
     * @param cipherAlgorithm 解密算法
     * @param iv      初始化向量
     * @return 解密后的内容
     * @throws Exception 异常
     */
    public static String decode(String payload, String key, String cipherAlgorithm, byte[] iv) throws Exception {
        byte[] bytes = HEX.toBytes(payload);
        byte[] keyBytes = key.getBytes(DEFAULT_CHARSET);
        return new String(decrypt(bytes, keyBytes, cipherAlgorithm, iv));
    }
    
    /**
     * 解密,参数为 AES 加密后的hex内容。默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待解密的hex 字符串
     * @param key     解密密码
     * @return 解密后的内容
     * @throws Exception 异常
     */
    public static String decode(String payload, String key) throws Exception {
        byte[] bytes = HEX.toBytes(payload);
        byte[] keyBytes = key.getBytes(DEFAULT_CHARSET);
        return new String(decrypt(bytes, keyBytes));
    }
    
    /**
     * 解密,参数为 AES 加密后的hex内容。默认使用 AES/ECB/PKCS5Padding
     *
     * @param payload 待解密的hex 字符串
     * @param key     解密密码
     * @param iv      初始化向量
     * @return 解密后的内容
     * @throws Exception 异常
     */
    public static String decode(String payload, String key, byte[] iv) throws Exception {
        byte[] bytes = HEX.toBytes(payload);
        byte[] keyBytes = key.getBytes(DEFAULT_CHARSET);
        return new String(decrypt(bytes, keyBytes, iv));
    }
}
